using System;
using System.Collections.Generic;
using Afru.Components.Messaging;

namespace Afru
{
    public class GameScreen
    {
        #region Variables

        #region Private variables


        // Set to internal so that the Engine class can access it without allowing
        // other classes to set Engine. Engine must be set through
        // the Engine's PushGameScreen() method so that the stack
        // can be maintained
        internal Engine engine = null;

        // The list of components that the gamescreen manages
        List<Component> components = new List<Component>();

        //Are all of the components loaded?
        bool loaded = false;

        #endregion

        #region Public variables

        /// <summary>
        /// Controls whether the Gamescreen can update.
        /// </summary>
        public bool Active = true; 
        /// <summary>
        /// Controls whether the Gamescreen can draw.
        /// </summary>
        public bool Visible = true;//Can the screen draw?

        /// <summary>
        /// Does the screen have an on transition?
        /// </summary>
        public bool HasOnTransition = false;
        /// <summary>
        /// Does the screen have an off transition?
        /// </summary>
        public bool HasOffTransition = false;

        /// <summary>
        /// The GLEE2D level that the gamescreen will draw.
        /// </summary>
        //public Level Level { get { return level; } set { level = value; } }

        /// <summary>
        /// Used to determine which drawing code the gamescreen will use
        /// </summary>
        public enum ScreenState
        {
            /// <summary>
            /// The screen is transitioning on
            /// </summary>
            TransitionOn,
            /// <summary>
            /// The screen is transistioning off
            /// </summary>
            TransitionOff,
            /// <summary>
            /// The screen is transitioning off, when it finishes, the game will quit
            /// </summary>
            TransitionOffToQuit,
            /// <summary>
            /// The screen is active, normal Update() and Draw() are called
            /// </summary>
            Active,
            /// <summary>
            /// THe screen is inactive, it isn't drawn or updated
            /// </summary>
            Inactive
            //TODO Implement inactive Screenstate
        }

        /// <summary>
        /// Used to time transitions.
        /// </summary>
        public TimeSpan TransitionTime = TimeSpan.Zero;

        /// <summary>
        /// How long the Gamescreen will transition on for in seconds.
        /// </summary>
        public float TransitionOnLength = 1;

        /// <summary>
        /// How long the Gamescreen will transition off for in seconds.
        /// </summary>
        public float TransitionOffLength = 1;

        /// <summary>
        /// The current screen state of the screen.
        /// </summary>
        public ScreenState CurrentScreenState;

        /// <summary>
        /// The Engine that this gamescreen belongs to.
        /// </summary>
        public Engine Engine
        {
            get { return engine; }
        }

        /// <summary>
        /// Returns the loading state of the Gamescreen. True for all data loaded, false otherwise.
        /// </summary>
        public bool Loaded { get { return loaded; } }

        // Allow for components to be retrieved with the [] index, ie:
        // Component c = gameScreenInstance["Component1"];
        /// <summary>
        /// Search for a component by name
        /// </summary>
        /// <param name="Name">The name of the component to be returned</param>
        /// <returns>The specified component if found, null if the compoenent doesn't exist</returns>
        public Component this[string Name]
        {
            get
            {
                foreach (Component component in components)
                    if (component.Name == Name)
                        return component;

                return null;
            }
        }

        #endregion

        #endregion

        #region Constructor

        public GameScreen()
        {
        }

        #endregion

        #region Class Functions

        #region Component Functions

        /// <summary>
        /// Add a new component to the Gamescreen.
        /// </summary>
        /// <param name="Component">The component to add</param>
        public void AddComponent(Component Component)
        {
            if (!components.Contains(Component))
            {
                Component.Parent = this;
                Component.ParentEngine = this.engine;
                Component.LoadComponent();
                PutComponentsInOrder(Component);
                components.Add(Component);
            }
        }

        /// <summary>
        /// Add a new component to the gamescreen.
        /// </summary>
        /// <param name="Component">The component to add</param>
        /// <param name="Active">Can the component update?</param>
        /// <param name="Visible">Can the component draw?</param>
        public void AddComponent(Component Component, bool Active, bool Visible)
        {
            if (!components.Contains(Component))
            {
                components.Add(Component);
                Component.Parent = this;
                Component.ParentEngine = this.engine;
                Component.Active = Active;
                Component.Visible = Visible;
                Component.LoadComponent();
                PutComponentsInOrder(Component);
            }
        }

        // The components are stored in their draw order, so it is easy to loop 
        // through them and draw them in the correct order without having to sort
        // them every time they are drawn
        /// <summary>
        /// Sorts the Gamescreen's by their draw order.
        /// </summary>
        /// <param name="component">The component to insert</param>
        public void PutComponentsInOrder(Component component)
        {
            if (components.Contains(component))
            {
                components.Remove(component);

                int i = 0;

                // TODO The component sorting uses a linear search, consider implementing a better sorting algorithm

                // Iterate through the components in order until we find one with
                // a higher or equal draw order, and insert the component at that
                // position.
                for (i = 0; i < components.Count; i++)
                    if (components[i].DrawOrder >= component.DrawOrder)
                        break;

                components.Insert(i, component);
            }
        }

        /// <summary>
        /// Remove a component by it's name.
        /// </summary>
        /// <param name="Name">The name of the component to remove</param>
        public void RemoveComponentByName(string Name)
        {
            Component c = this[Name];
            c.OnRemove();
            RemoveComponent(c);
            //TODO Test RemoveComponentByName()
        }

        /// <summary>
        /// Remove a component from Gamescreen
        /// </summary>
        /// <param name="Component">The component instance to remove</param>
        public void RemoveComponent(Component Component)
        {
            if (Component != null && components.Contains(Component))
            {
                Component.OnRemove();
                components.Remove(Component);
                Component.Parent = null;
            }
        }

        #endregion

        #region Transition Related Functions

        /// <summary>
        /// Gamescreen initialization code goes here.
        /// </summary>
        public virtual void OnEnter()
        {
            //Reset the transition time to zero so that the transition can work properly
            TransitionTime = TimeSpan.Zero;
        }

        /// <summary>
        /// Gamescreen exit code goes here.
        /// </summary>
        public virtual void OnExit()
        {
            //Reset the transition time to zero so that the transition can work properly
            TransitionTime = TimeSpan.Zero;
        }

        /// <summary>
        /// Program your transition on code here.
        /// </summary>
        public virtual void TransitionOn()
        {
            if (!HasOnTransition)
                CurrentScreenState = ScreenState.Active;

            if (TransitionTime.TotalSeconds > TransitionOnLength)
                this.CurrentScreenState = ScreenState.Active;
        }

        public virtual void UpdateTransitionOn()
        {
            TransitionTime += Engine.GameTime.ElapsedGameTime;
        }

        /// <summary>
        /// Put your transition off code here.
        /// </summary>
        public virtual void TransitionOff()
        {
            if (!HasOffTransition)
                CurrentScreenState = ScreenState.Inactive;

            if (TransitionTime.TotalSeconds > TransitionOffLength)
                this.CurrentScreenState = ScreenState.Inactive;
        }

        public virtual void UpdateTransitionOff()
        {
            TransitionTime += Engine.GameTime.ElapsedGameTime;
        }

        /// <summary>
        /// Same as TransitionOff(), but will exit the game once the transition is complete.
        /// </summary>
        public virtual void TransitionOffToQuit()
        {
            if (!HasOffTransition)
                engine.Exit();

            if (TransitionTime.TotalSeconds > TransitionOffLength)
                Engine.Exit();
        }

        #endregion

        #region Activate and Deactivate

        // These functions will force Update() and Draw() to stop or resume getting called.

        /// <summary>
        /// Gamescreen will call both Update() and Draw()
        /// </summary>
        public void Activate()
        {
            Active = true;
            Visible = true;
        }

        /// <summary>
        /// Gamescreen will not update or draw.
        /// </summary>
        public void Deactivate()
        {
            Active = false;
            Visible = false;     
        }

        #endregion

        #region Overrideable functions

        //TODO Document the RecieveMessage() function
        /// <summary>
        /// ???
        /// </summary>
        /// <param name="Message">??</param>
        public virtual void ReceiveMessage(AMessage Message)
        {

        }

        /// <summary>
        /// Loads all of the data associated with the Gamescreen.
        /// </summary>
        public void LoadGameScreen()
        {
            if (loaded)
                return;

            loaded = true;

            Load();
        }

        //Load() is used by the class's children
        protected virtual void Load()
        {
            engine.Messaging.MessageHandler += new MessageReceivedEventHandler(ReceiveMessage);
        }

        public virtual void Update()
        {
            // Copy the list of components so the game won't crash if the original list
            // is modified while updating
            List<Component> updating = new List<Component>();

            foreach (Component c in components)
                if (!updating.Contains(c))
                    updating.Add(c);

            foreach (Component c in updating)
            {
                if (c.Active)
                    c.Update();
            }
        }

        public virtual void Draw()
        {
            foreach (Component c in components)
            {
                if (c.Visible)
                    c.Draw();
            }
        }

        #endregion

        #endregion 
    }
}
